﻿using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

namespace CWM.Skinn
{
    public static class DragAndDropMerge
    {
        private static List<GameObject> draggedObjects = new List<GameObject>();
        private static GameObject contextRoot = null;

        private static class MergeCommand
        {
            public const string 
                All = "Merge",
                AllBindpose = "Merge All Bind-posed",
                Cancel = "Cancel",
                Continue = "Continue";
        }

        public static void HierarchyDrag(int instanceID, Rect selectionRect)
        {
            Event currentEvent = Event.current;
            switch (currentEvent.type)
            {

                case EventType.DragUpdated: { } break;
                case EventType.DragPerform:
                    if (!selectionRect.Contains(currentEvent.mousePosition)) { return; }
                    DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
                    if (currentEvent.type == EventType.DragPerform)
                    {
                        contextRoot = SkinnEx.FindGameObject(instanceID);
                        if (!contextRoot) break;

                        UnityEngine.Object[] objectReferences = DragAndDrop.objectReferences;
                        draggedObjects = new List<GameObject>();
                        int skinnedMeshCount = 0;
                        for (int i = 0; i < objectReferences.Length; i++)
                        {
                            var go = objectReferences[i] as GameObject;
                            if (!go || string.IsNullOrEmpty(go.name)) continue;
                            if (!draggedObjects.Contains(go) && go.scene != contextRoot.scene)
                            {
                                draggedObjects.Add(go);
                                skinnedMeshCount += go.GetValidSkinnedMeshes().Count;
                            }
                        }

                        if (draggedObjects.Count > 0 && skinnedMeshCount > 0)
                        {
                            if (!contextRoot) draggedObjects = new List<GameObject>();
                            else {  ShowContextMenu(); Event.current.Use(); EditorGUIUtility.SetWantsMouseJumping(0);  }
                        }
                    }
                    break;
            }
            //GUI
        }

        private static string RemoveSpecialCharacters(string value)
        {
            string newString = "";
            value = value.Replace(" & ", " And ");
            value = value.Replace("&", " And ");

            foreach (var item in value)
            {
                if (char.IsLetterOrDigit(item) || char.IsWhiteSpace(item)) newString += item;
            }
            return newString;
        }

        private static void ShowContextMenu()
        {
            GenericMenu menu = new GenericMenu();
            menu.AddDisabledItem(new GUIContent(RemoveSpecialCharacters(contextRoot.name)));
            
            //menu.AddDisabledItem(new GUIContent(contextRoot.name), contextRoot.activeInHierarchy);
            menu.AddSeparator("");
            menu.AddItem(new GUIContent(MergeCommand.All), false, ContextMenuSelected, MergeCommand.All);
            menu.AddItem(new GUIContent(MergeCommand.AllBindpose), false, ContextMenuSelected, MergeCommand.AllBindpose);
            menu.AddSeparator("");
            for (int i = 0; i < draggedObjects.Count; i++)
            {
                var root = draggedObjects[i]; if (!root) continue;
                var meshes = root.GetValidSkinnedMeshes();
                for (int ii = 0; ii < meshes.Count; ii++)
                {
                    var mesh = meshes[ii]; if (!mesh) continue;
                    var guiContent = new GUIContent(string.Format("{0}/{1}", SkinnEx.EnforceObjectName(root), SkinnEx.EnforceObjectName(mesh)));
                    menu.AddItem(guiContent, false, ContextMenuSelected, mesh);
                }
            }
            menu.AddSeparator("");

            menu.AddItem(new GUIContent(MergeCommand.Cancel), false, ContextMenuSelected, MergeCommand.Cancel);
            menu.AddItem(new GUIContent(MergeCommand.Continue), false, ContextMenuSelected, MergeCommand.Continue);

            menu.ShowAsContext();
        }

        private static void ContextMenuSelected(object id)
        {
            if (!contextRoot || SkinnEx.IsNullOrEmpty(draggedObjects)) return;
            string textId; try { textId = id as string; } catch { textId = ""; }
            SkinnedMeshRenderer skinnedMeshID; try { skinnedMeshID = id as SkinnedMeshRenderer; } catch { skinnedMeshID = null; }

            switch (textId)
            {
                case MergeCommand.All:
                    for (int i = 0; i < draggedObjects.Count; i++)
                    {
                        var go = draggedObjects[i]; if (!go) continue; var skinnedMeshes = go.GetValidSkinnedMeshes();
                        foreach (var item in skinnedMeshes) MergeSkinnedMeshes(item, false);
                    }
                    return;
                case MergeCommand.AllBindpose:
                        for (int i = 0; i < draggedObjects.Count; i++)
                        {
                            var go = draggedObjects[i]; if (!go) continue; var skinnedMeshes = go.GetValidSkinnedMeshes();
                            foreach (var item in skinnedMeshes) MergeSkinnedMeshes(item, true);
                        }
                    return;
                case MergeCommand.Cancel: return;
                case MergeCommand.Continue:
                case "":
                default: break;
            }

            if (skinnedMeshID)
            {
                MergeSkinnedMeshes(skinnedMeshID, false);
                return;
            }

            for (int i = 0; i < draggedObjects.Count; i++)
            {
                var go = draggedObjects[i]; if (!go) continue;
                GameObject instance = PrefabUtility.InstantiatePrefab(go) as GameObject; if (!instance) continue;
                Undo.RegisterCreatedObjectUndo(instance, "Instantiate Prefab");
                instance.transform.SetParent(contextRoot.transform, true);
            }
        }

        private static void MergeSkinnedMeshes(SkinnedMeshRenderer smr, bool bindpose = false)
        {
            List<Transform> createdObjects;
            SkinnedMeshRenderer clone;

            if (bindpose) clone = smr.CloneMerged(contextRoot.transform, out createdObjects, true, false);
            else clone = smr.CloneMerged(contextRoot.transform, out createdObjects);
            if (!clone) return;

            for (int i = 0; i < createdObjects.Count; i++) Undo.RegisterCreatedObjectUndo(createdObjects[i].gameObject, "Instantiate :" + MergeCommand.AllBindpose);
        }
    }
}